<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2009 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Think\Crypt\Driver;

/**
 * Des 加密实现类
 * Converted from JavaScript to PHP by Jim Gibbs, June 2004 Paul Tero, July 2001
 * Optimised for performance with large blocks by Michael Hayworth, November 2001
 * http://www.netdealing.com
 */
class Des
{

    /**
     * 加密字符串
     * 
     * @param string $str
     *            字符串
     * @param string $key
     *            加密key
     * @param integer $expire
     *            有效期（秒）
     * @return string
     */
    public static function encrypt($str, $key, $expire = 0)
    {
        if ($str == "") {
            return "";
        }
        $expire = sprintf('%010d', $expire ? $expire + time() : 0);
        $str = $expire . $str;
        return self::_des($key, $str, 1);
    }

    /**
     * 解密字符串
     * 
     * @param string $str
     *            字符串
     * @param string $key
     *            加密key
     * @return string
     */
    public static function decrypt($str, $key)
    {
        if ($str == "") {
            return "";
        }
        $data = self::_des($key, $str, 0);
        $expire = substr($data, 0, 10);
        if ($expire > 0 && $expire < time()) {
            return '';
        }
        $data = substr($data, 10);
        return $data;
    }

    /**
     * Des算法
     * 
     * @param string $str
     *            字符串
     * @param string $key
     *            加密key
     * @return string
     */
    private static function _des($key, $message, $encrypt, $mode = 0, $iv = null)
    {
        // declaring this locally speeds things up a bit
        $spfunction1 = array(
            0x1010400,
            0,
            0x10000,
            0x1010404,
            0x1010004,
            0x10404,
            0x4,
            0x10000,
            0x400,
            0x1010400,
            0x1010404,
            0x400,
            0x1000404,
            0x1010004,
            0x1000000,
            0x4,
            0x404,
            0x1000400,
            0x1000400,
            0x10400,
            0x10400,
            0x1010000,
            0x1010000,
            0x1000404,
            0x10004,
            0x1000004,
            0x1000004,
            0x10004,
            0,
            0x404,
            0x10404,
            0x1000000,
            0x10000,
            0x1010404,
            0x4,
            0x1010000,
            0x1010400,
            0x1000000,
            0x1000000,
            0x400,
            0x1010004,
            0x10000,
            0x10400,
            0x1000004,
            0x400,
            0x4,
            0x1000404,
            0x10404,
            0x1010404,
            0x10004,
            0x1010000,
            0x1000404,
            0x1000004,
            0x404,
            0x10404,
            0x1010400,
            0x404,
            0x1000400,
            0x1000400,
            0,
            0x10004,
            0x10400,
            0,
            0x1010004
        );
        $spfunction2 = array(
            - 0x7fef7fe0,
            - 0x7fff8000,
            0x8000,
            0x108020,
            0x100000,
            0x20,
            - 0x7fefffe0,
            - 0x7fff7fe0,
            - 0x7fffffe0,
            - 0x7fef7fe0,
            - 0x7fef8000,
            - 0x80000000,
            - 0x7fff8000,
            0x100000,
            0x20,
            - 0x7fefffe0,
            0x108000,
            0x100020,
            - 0x7fff7fe0,
            0,
            - 0x80000000,
            0x8000,
            0x108020,
            - 0x7ff00000,
            0x100020,
            - 0x7fffffe0,
            0,
            0x108000,
            0x8020,
            - 0x7fef8000,
            - 0x7ff00000,
            0x8020,
            0,
            0x108020,
            - 0x7fefffe0,
            0x100000,
            - 0x7fff7fe0,
            - 0x7ff00000,
            - 0x7fef8000,
            0x8000,
            - 0x7ff00000,
            - 0x7fff8000,
            0x20,
            - 0x7fef7fe0,
            0x108020,
            0x20,
            0x8000,
            - 0x80000000,
            0x8020,
            - 0x7fef8000,
            0x100000,
            - 0x7fffffe0,
            0x100020,
            - 0x7fff7fe0,
            - 0x7fffffe0,
            0x100020,
            0x108000,
            0,
            - 0x7fff8000,
            0x8020,
            - 0x80000000,
            - 0x7fefffe0,
            - 0x7fef7fe0,
            0x108000
        );
        $spfunction3 = array(
            0x208,
            0x8020200,
            0,
            0x8020008,
            0x8000200,
            0,
            0x20208,
            0x8000200,
            0x20008,
            0x8000008,
            0x8000008,
            0x20000,
            0x8020208,
            0x20008,
            0x8020000,
            0x208,
            0x8000000,
            0x8,
            0x8020200,
            0x200,
            0x20200,
            0x8020000,
            0x8020008,
            0x20208,
            0x8000208,
            0x20200,
            0x20000,
            0x8000208,
            0x8,
            0x8020208,
            0x200,
            0x8000000,
            0x8020200,
            0x8000000,
            0x20008,
            0x208,
            0x20000,
            0x8020200,
            0x8000200,
            0,
            0x200,
            0x20008,
            0x8020208,
            0x8000200,
            0x8000008,
            0x200,
            0,
            0x8020008,
            0x8000208,
            0x20000,
            0x8000000,
            0x8020208,
            0x8,
            0x20208,
            0x20200,
            0x8000008,
            0x8020000,
            0x8000208,
            0x208,
            0x8020000,
            0x20208,
            0x8,
            0x8020008,
            0x20200
        );
        $spfunction4 = array(
            0x802001,
            0x2081,
            0x2081,
            0x80,
            0x802080,
            0x800081,
            0x800001,
            0x2001,
            0,
            0x802000,
            0x802000,
            0x802081,
            0x81,
            0,
            0x800080,
            0x800001,
            0x1,
            0x2000,
            0x800000,
            0x802001,
            0x80,
            0x800000,
            0x2001,
            0x2080,
            0x800081,
            0x1,
            0x2080,
            0x800080,
            0x2000,
            0x802080,
            0x802081,
            0x81,
            0x800080,
            0x800001,
            0x802000,
            0x802081,
            0x81,
            0,
            0,
            0x802000,
            0x2080,
            0x800080,
            0x800081,
            0x1,
            0x802001,
            0x2081,
            0x2081,
            0x80,
            0x802081,
            0x81,
            0x1,
            0x2000,
            0x800001,
            0x2001,
            0x802080,
            0x800081,
            0x2001,
            0x2080,
            0x800000,
            0x802001,
            0x80,
            0x800000,
            0x2000,
            0x802080
        );
        $spfunction5 = array(
            0x100,
            0x2080100,
            0x2080000,
            0x42000100,
            0x80000,
            0x100,
            0x40000000,
            0x2080000,
            0x40080100,
            0x80000,
            0x2000100,
            0x40080100,
            0x42000100,
            0x42080000,
            0x80100,
            0x40000000,
            0x2000000,
            0x40080000,
            0x40080000,
            0,
            0x40000100,
            0x42080100,
            0x42080100,
            0x2000100,
            0x42080000,
            0x40000100,
            0,
            0x42000000,
            0x2080100,
            0x2000000,
            0x42000000,
            0x80100,
            0x80000,
            0x42000100,
            0x100,
            0x2000000,
            0x40000000,
            0x2080000,
            0x42000100,
            0x40080100,
            0x2000100,
            0x40000000,
            0x42080000,
            0x2080100,
            0x40080100,
            0x100,
            0x2000000,
            0x42080000,
            0x42080100,
            0x80100,
            0x42000000,
            0x42080100,
            0x2080000,
            0,
            0x40080000,
            0x42000000,
            0x80100,
            0x2000100,
            0x40000100,
            0x80000,
            0,
            0x40080000,
            0x2080100,
            0x40000100
        );
        $spfunction6 = array(
            0x20000010,
            0x20400000,
            0x4000,
            0x20404010,
            0x20400000,
            0x10,
            0x20404010,
            0x400000,
            0x20004000,
            0x404010,
            0x400000,
            0x20000010,
            0x400010,
            0x20004000,
            0x20000000,
            0x4010,
            0,
            0x400010,
            0x20004010,
            0x4000,
            0x404000,
            0x20004010,
            0x10,
            0x20400010,
            0x20400010,
            0,
            0x404010,
            0x20404000,
            0x4010,
            0x404000,
            0x20404000,
            0x20000000,
            0x20004000,
            0x10,
            0x20400010,
            0x404000,
            0x20404010,
            0x400000,
            0x4010,
            0x20000010,
            0x400000,
            0x20004000,
            0x20000000,
            0x4010,
            0x20000010,
            0x20404010,
            0x404000,
            0x20400000,
            0x404010,
            0x20404000,
            0,
            0x20400010,
            0x10,
            0x4000,
            0x20400000,
            0x404010,
            0x4000,
            0x400010,
            0x20004010,
            0,
            0x20404000,
            0x20000000,
            0x400010,
            0x20004010
        );
        $spfunction7 = array(
            0x200000,
            0x4200002,
            0x4000802,
            0,
            0x800,
            0x4000802,
            0x200802,
            0x4200800,
            0x4200802,
            0x200000,
            0,
            0x4000002,
            0x2,
            0x4000000,
            0x4200002,
            0x802,
            0x4000800,
            0x200802,
            0x200002,
            0x4000800,
            0x4000002,
            0x4200000,
            0x4200800,
            0x200002,
            0x4200000,
            0x800,
            0x802,
            0x4200802,
            0x200800,
            0x2,
            0x4000000,
            0x200800,
            0x4000000,
            0x200800,
            0x200000,
            0x4000802,
            0x4000802,
            0x4200002,
            0x4200002,
            0x2,
            0x200002,
            0x4000000,
            0x4000800,
            0x200000,
            0x4200800,
            0x802,
            0x200802,
            0x4200800,
            0x802,
            0x4000002,
            0x4200802,
            0x4200000,
            0x200800,
            0,
            0x2,
            0x4200802,
            0,
            0x200802,
            0x4200000,
            0x800,
            0x4000002,
            0x4000800,
            0x800,
            0x200002
        );
        $spfunction8 = array(
            0x10001040,
            0x1000,
            0x40000,
            0x10041040,
            0x10000000,
            0x10001040,
            0x40,
            0x10000000,
            0x40040,
            0x10040000,
            0x10041040,
            0x41000,
            0x10041000,
            0x41040,
            0x1000,
            0x40,
            0x10040000,
            0x10000040,
            0x10001000,
            0x1040,
            0x41000,
            0x40040,
            0x10040040,
            0x10041000,
            0x1040,
            0,
            0,
            0x10040040,
            0x10000040,
            0x10001000,
            0x41040,
            0x40000,
            0x41040,
            0x40000,
            0x10041000,
            0x1000,
            0x40,
            0x10040040,
            0x1000,
            0x41040,
            0x10001000,
            0x40,
            0x10000040,
            0x10040000,
            0x10040040,
            0x10000000,
            0x40000,
            0x10001040,
            0,
            0x10041040,
            0x40040,
            0x10000040,
            0x10040000,
            0x10001000,
            0x10001040,
            0,
            0x10041040,
            0x41000,
            0x41000,
            0x1040,
            0x1040,
            0x40040,
            0x10000000,
            0x10041000
        );
        $masks = array(
            4294967295,
            2147483647,
            1073741823,
            536870911,
            268435455,
            134217727,
            67108863,
            33554431,
            16777215,
            8388607,
            4194303,
            2097151,
            1048575,
            524287,
            262143,
            131071,
            65535,
            32767,
            16383,
            8191,
            4095,
            2047,
            1023,
            511,
            255,
            127,
            63,
            31,
            15,
            7,
            3,
            1,
            0
        );
        
        // create the 16 or 48 subkeys we will need
        $keys = self::_createKeys($key);
        $m = 0;
        $len = strlen($message);
        $chunk = 0;
        // set up the loops for single and triple des
        $iterations = ((count($keys) == 32) ? 3 : 9); // single or triple des
        if ($iterations == 3) {
            $looping = (($encrypt) ? array(
                0,
                32,
                2
            ) : array(
                30,
                - 2,
                - 2
            ));
        } else {
            $looping = (($encrypt) ? array(
                0,
                32,
                2,
                62,
                30,
                - 2,
                64,
                96,
                2
            ) : array(
                94,
                62,
                - 2,
                32,
                64,
                2,
                30,
                - 2,
                - 2
            ));
        }
        
        $message .= (chr(0) . chr(0) . chr(0) . chr(0) . chr(0) . chr(0) . chr(0) . chr(0)); // pad the message out with null bytes
                                                                                             // store the result here
        $result = "";
        $tempresult = "";
        
        if ($mode == 1) { // CBC mode
            $cbcleft = (ord($iv{$m ++}) << 24) | (ord($iv{$m ++}) << 16) | (ord($iv{$m ++}) << 8) | ord($iv{$m ++});
            $cbcright = (ord($iv{$m ++}) << 24) | (ord($iv{$m ++}) << 16) | (ord($iv{$m ++}) << 8) | ord($iv{$m ++});
            $m = 0;
        }
        
        // loop through each 64 bit chunk of the message
        while ($m < $len) {
            $left = (ord($message{$m ++}) << 24) | (ord($message{$m ++}) << 16) | (ord($message{$m ++}) << 8) | ord($message{$m ++});
            $right = (ord($message{$m ++}) << 24) | (ord($message{$m ++}) << 16) | (ord($message{$m ++}) << 8) | ord($message{$m ++});
            
            // for Cipher Block Chaining mode, xor the message with the previous result
            if ($mode == 1) {
                if ($encrypt) {
                    $left ^= $cbcleft;
                    $right ^= $cbcright;
                } else {
                    $cbcleft2 = $cbcleft;
                    $cbcright2 = $cbcright;
                    $cbcleft = $left;
                    $cbcright = $right;
                }
            }
            
            // first each 64 but chunk of the message must be permuted according to IP
            $temp = (($left >> 4 & $masks[4]) ^ $right) & 0x0f0f0f0f;
            $right ^= $temp;
            $left ^= ($temp << 4);
            $temp = (($left >> 16 & $masks[16]) ^ $right) & 0x0000ffff;
            $right ^= $temp;
            $left ^= ($temp << 16);
            $temp = (($right >> 2 & $masks[2]) ^ $left) & 0x33333333;
            $left ^= $temp;
            $right ^= ($temp << 2);
            $temp = (($right >> 8 & $masks[8]) ^ $left) & 0x00ff00ff;
            $left ^= $temp;
            $right ^= ($temp << 8);
            $temp = (($left >> 1 & $masks[1]) ^ $right) & 0x55555555;
            $right ^= $temp;
            $left ^= ($temp << 1);
            
            $left = (($left << 1) | ($left >> 31 & $masks[31]));
            $right = (($right << 1) | ($right >> 31 & $masks[31]));
            
            // do this either 1 or 3 times for each chunk of the message
            for ($j = 0; $j < $iterations; $j += 3) {
                $endloop = $looping[$j + 1];
                $loopinc = $looping[$j + 2];
                // now go through and perform the encryption or decryption
                for ($i = $looping[$j]; $i != $endloop; $i += $loopinc) { // for efficiency
                    $right1 = $right ^ $keys[$i];
                    $right2 = (($right >> 4 & $masks[4]) | ($right << 28)) ^ $keys[$i + 1];
                    // the result is attained by passing these bytes through the S selection functions
                    $temp = $left;
                    $left = $right;
                    $right = $temp ^ ($spfunction2[($right1 >> 24 & $masks[24]) & 0x3f] | $spfunction4[($right1 >> 16 & $masks[16]) & 0x3f] | $spfunction6[($right1 >> 8 & $masks[8]) & 0x3f] | $spfunction8[$right1 & 0x3f] | $spfunction1[($right2 >> 24 & $masks[24]) & 0x3f] | $spfunction3[($right2 >> 16 & $masks[16]) & 0x3f] | $spfunction5[($right2 >> 8 & $masks[8]) & 0x3f] | $spfunction7[$right2 & 0x3f]);
                }
                $temp = $left;
                $left = $right;
                $right = $temp; // unreverse left and right
            } // for either 1 or 3 iterations
              
            // move then each one bit to the right
            $left = (($left >> 1 & $masks[1]) | ($left << 31));
            $right = (($right >> 1 & $masks[1]) | ($right << 31));
            
            // now perform IP-1, which is IP in the opposite direction
            $temp = (($left >> 1 & $masks[1]) ^ $right) & 0x55555555;
            $right ^= $temp;
            $left ^= ($temp << 1);
            $temp = (($right >> 8 & $masks[8]) ^ $left) & 0x00ff00ff;
            $left ^= $temp;
            $right ^= ($temp << 8);
            $temp = (($right >> 2 & $masks[2]) ^ $left) & 0x33333333;
            $left ^= $temp;
            $right ^= ($temp << 2);
            $temp = (($left >> 16 & $masks[16]) ^ $right) & 0x0000ffff;
            $right ^= $temp;
            $left ^= ($temp << 16);
            $temp = (($left >> 4 & $masks[4]) ^ $right) & 0x0f0f0f0f;
            $right ^= $temp;
            $left ^= ($temp << 4);
            
            // for Cipher Block Chaining mode, xor the message with the previous result
            if ($mode == 1) {
                if ($encrypt) {
                    $cbcleft = $left;
                    $cbcright = $right;
                } else {
                    $left ^= $cbcleft2;
                    $right ^= $cbcright2;
                }
            }
            $tempresult .= (chr($left >> 24 & $masks[24]) . chr(($left >> 16 & $masks[16]) & 0xff) . chr(($left >> 8 & $masks[8]) & 0xff) . chr($left & 0xff) . chr($right >> 24 & $masks[24]) . chr(($right >> 16 & $masks[16]) & 0xff) . chr(($right >> 8 & $masks[8]) & 0xff) . chr($right & 0xff));
            
            $chunk += 8;
            if ($chunk == 512) {
                $result .= $tempresult;
                $tempresult = "";
                $chunk = 0;
            }
        } // for every 8 characters, or 64 bits in the message
          
        // return the result as an array
        return ($result . $tempresult);
    }
 // end of des
    
    /**
     * createKeys
     * this takes as input a 64 bit key (even though only 56 bits are used)
     * as an array of 2 integers, and returns 16 48 bit keys
     * 
     * @param string $key
     *            加密key
     * @return string
     */
    private static function _createKeys($key)
    {
        // declaring this locally speeds things up a bit
        $pc2bytes0 = array(
            0,
            0x4,
            0x20000000,
            0x20000004,
            0x10000,
            0x10004,
            0x20010000,
            0x20010004,
            0x200,
            0x204,
            0x20000200,
            0x20000204,
            0x10200,
            0x10204,
            0x20010200,
            0x20010204
        );
        $pc2bytes1 = array(
            0,
            0x1,
            0x100000,
            0x100001,
            0x4000000,
            0x4000001,
            0x4100000,
            0x4100001,
            0x100,
            0x101,
            0x100100,
            0x100101,
            0x4000100,
            0x4000101,
            0x4100100,
            0x4100101
        );
        $pc2bytes2 = array(
            0,
            0x8,
            0x800,
            0x808,
            0x1000000,
            0x1000008,
            0x1000800,
            0x1000808,
            0,
            0x8,
            0x800,
            0x808,
            0x1000000,
            0x1000008,
            0x1000800,
            0x1000808
        );
        $pc2bytes3 = array(
            0,
            0x200000,
            0x8000000,
            0x8200000,
            0x2000,
            0x202000,
            0x8002000,
            0x8202000,
            0x20000,
            0x220000,
            0x8020000,
            0x8220000,
            0x22000,
            0x222000,
            0x8022000,
            0x8222000
        );
        $pc2bytes4 = array(
            0,
            0x40000,
            0x10,
            0x40010,
            0,
            0x40000,
            0x10,
            0x40010,
            0x1000,
            0x41000,
            0x1010,
            0x41010,
            0x1000,
            0x41000,
            0x1010,
            0x41010
        );
        $pc2bytes5 = array(
            0,
            0x400,
            0x20,
            0x420,
            0,
            0x400,
            0x20,
            0x420,
            0x2000000,
            0x2000400,
            0x2000020,
            0x2000420,
            0x2000000,
            0x2000400,
            0x2000020,
            0x2000420
        );
        $pc2bytes6 = array(
            0,
            0x10000000,
            0x80000,
            0x10080000,
            0x2,
            0x10000002,
            0x80002,
            0x10080002,
            0,
            0x10000000,
            0x80000,
            0x10080000,
            0x2,
            0x10000002,
            0x80002,
            0x10080002
        );
        $pc2bytes7 = array(
            0,
            0x10000,
            0x800,
            0x10800,
            0x20000000,
            0x20010000,
            0x20000800,
            0x20010800,
            0x20000,
            0x30000,
            0x20800,
            0x30800,
            0x20020000,
            0x20030000,
            0x20020800,
            0x20030800
        );
        $pc2bytes8 = array(
            0,
            0x40000,
            0,
            0x40000,
            0x2,
            0x40002,
            0x2,
            0x40002,
            0x2000000,
            0x2040000,
            0x2000000,
            0x2040000,
            0x2000002,
            0x2040002,
            0x2000002,
            0x2040002
        );
        $pc2bytes9 = array(
            0,
            0x10000000,
            0x8,
            0x10000008,
            0,
            0x10000000,
            0x8,
            0x10000008,
            0x400,
            0x10000400,
            0x408,
            0x10000408,
            0x400,
            0x10000400,
            0x408,
            0x10000408
        );
        $pc2bytes10 = array(
            0,
            0x20,
            0,
            0x20,
            0x100000,
            0x100020,
            0x100000,
            0x100020,
            0x2000,
            0x2020,
            0x2000,
            0x2020,
            0x102000,
            0x102020,
            0x102000,
            0x102020
        );
        $pc2bytes11 = array(
            0,
            0x1000000,
            0x200,
            0x1000200,
            0x200000,
            0x1200000,
            0x200200,
            0x1200200,
            0x4000000,
            0x5000000,
            0x4000200,
            0x5000200,
            0x4200000,
            0x5200000,
            0x4200200,
            0x5200200
        );
        $pc2bytes12 = array(
            0,
            0x1000,
            0x8000000,
            0x8001000,
            0x80000,
            0x81000,
            0x8080000,
            0x8081000,
            0x10,
            0x1010,
            0x8000010,
            0x8001010,
            0x80010,
            0x81010,
            0x8080010,
            0x8081010
        );
        $pc2bytes13 = array(
            0,
            0x4,
            0x100,
            0x104,
            0,
            0x4,
            0x100,
            0x104,
            0x1,
            0x5,
            0x101,
            0x105,
            0x1,
            0x5,
            0x101,
            0x105
        );
        $masks = array(
            4294967295,
            2147483647,
            1073741823,
            536870911,
            268435455,
            134217727,
            67108863,
            33554431,
            16777215,
            8388607,
            4194303,
            2097151,
            1048575,
            524287,
            262143,
            131071,
            65535,
            32767,
            16383,
            8191,
            4095,
            2047,
            1023,
            511,
            255,
            127,
            63,
            31,
            15,
            7,
            3,
            1,
            0
        );
        
        // how many iterations (1 for des, 3 for triple des)
        $iterations = ((strlen($key) >= 24) ? 3 : 1);
        // stores the return keys
        $keys = array(); // size = 32 * iterations but you don't specify this in php
                          // now define the left shifts which need to be done
        $shifts = array(
            0,
            0,
            1,
            1,
            1,
            1,
            1,
            1,
            0,
            1,
            1,
            1,
            1,
            1,
            1,
            0
        );
        // other variables
        $m = 0;
        $n = 0;
        
        for ($j = 0; $j < $iterations; $j ++) { // either 1 or 3 iterations
            $left = (ord($key{$m ++}) << 24) | (ord($key{$m ++}) << 16) | (ord($key{$m ++}) << 8) | ord($key{$m ++});
            $right = (ord($key{$m ++}) << 24) | (ord($key{$m ++}) << 16) | (ord($key{$m ++}) << 8) | ord($key{$m ++});
            
            $temp = (($left >> 4 & $masks[4]) ^ $right) & 0x0f0f0f0f;
            $right ^= $temp;
            $left ^= ($temp << 4);
            $temp = (($right >> 16 & $masks[16]) ^ $left) & 0x0000ffff;
            $left ^= $temp;
            $right ^= ($temp << - 16);
            $temp = (($left >> 2 & $masks[2]) ^ $right) & 0x33333333;
            $right ^= $temp;
            $left ^= ($temp << 2);
            $temp = (($right >> 16 & $masks[16]) ^ $left) & 0x0000ffff;
            $left ^= $temp;
            $right ^= ($temp << - 16);
            $temp = (($left >> 1 & $masks[1]) ^ $right) & 0x55555555;
            $right ^= $temp;
            $left ^= ($temp << 1);
            $temp = (($right >> 8 & $masks[8]) ^ $left) & 0x00ff00ff;
            $left ^= $temp;
            $right ^= ($temp << 8);
            $temp = (($left >> 1 & $masks[1]) ^ $right) & 0x55555555;
            $right ^= $temp;
            $left ^= ($temp << 1);
            
            // the right side needs to be shifted and to get the last four bits of the left side
            $temp = ($left << 8) | (($right >> 20 & $masks[20]) & 0x000000f0);
            // left needs to be put upside down
            $left = ($right << 24) | (($right << 8) & 0xff0000) | (($right >> 8 & $masks[8]) & 0xff00) | (($right >> 24 & $masks[24]) & 0xf0);
            $right = $temp;
            
            // now go through and perform these shifts on the left and right keys
            for ($i = 0; $i < count($shifts); $i ++) {
                // shift the keys either one or two bits to the left
                if ($shifts[$i] > 0) {
                    $left = (($left << 2) | ($left >> 26 & $masks[26]));
                    $right = (($right << 2) | ($right >> 26 & $masks[26]));
                } else {
                    $left = (($left << 1) | ($left >> 27 & $masks[27]));
                    $right = (($right << 1) | ($right >> 27 & $masks[27]));
                }
                $left = $left & - 0xf;
                $right = $right & - 0xf;
                
                // now apply PC-2, in such a way that E is easier when encrypting or decrypting
                // this conversion will look like PC-2 except only the last 6 bits of each byte are used
                // rather than 48 consecutive bits and the order of lines will be according to
                // how the S selection functions will be applied: S2, S4, S6, S8, S1, S3, S5, S7
                $lefttemp = $pc2bytes0[$left >> 28 & $masks[28]] | $pc2bytes1[($left >> 24 & $masks[24]) & 0xf] | $pc2bytes2[($left >> 20 & $masks[20]) & 0xf] | $pc2bytes3[($left >> 16 & $masks[16]) & 0xf] | $pc2bytes4[($left >> 12 & $masks[12]) & 0xf] | $pc2bytes5[($left >> 8 & $masks[8]) & 0xf] | $pc2bytes6[($left >> 4 & $masks[4]) & 0xf];
                $righttemp = $pc2bytes7[$right >> 28 & $masks[28]] | $pc2bytes8[($right >> 24 & $masks[24]) & 0xf] | $pc2bytes9[($right >> 20 & $masks[20]) & 0xf] | $pc2bytes10[($right >> 16 & $masks[16]) & 0xf] | $pc2bytes11[($right >> 12 & $masks[12]) & 0xf] | $pc2bytes12[($right >> 8 & $masks[8]) & 0xf] | $pc2bytes13[($right >> 4 & $masks[4]) & 0xf];
                $temp = (($righttemp >> 16 & $masks[16]) ^ $lefttemp) & 0x0000ffff;
                $keys[$n ++] = $lefttemp ^ $temp;
                $keys[$n ++] = $righttemp ^ ($temp << 16);
            }
        } // for each iterations
          // return the keys we've created
        return $keys;
    } // end of des_createKeys
}